package org.umlviewer.model.diagram_class;

import java.awt.*;
import java.util.ArrayList;
import org.umlviewer.model.DiagramElement;
import org.umlviewer.model.Constants;
import org.umlviewer.model.VisibilityKind;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.umlviewer.paint.PaintUtils;

public class Clase extends DiagramElement{
    
    private boolean bAstract;
    private ArrayList<Attribute> attributes;
    private ArrayList<Method> methods;
        
    private int width = 20;
    private int height = 0;
    private int numparams;
    //alto de un salto de linea
    private final int LHEIGHT = 20;
    //espacio ocupado por una letra
    private final int LETTER = 8;
    // el numero de objetos que se han instanciado
    private static int counter = 1;
    
    public Clase()
    {
        attributes = new ArrayList<Attribute>();
        methods = new ArrayList<Method>();
        setName("");
        bAstract= false;        
    }
    
    public Clase(String name)
    {
        setName(name);
        bAstract= false; 
        attributes = new ArrayList<Attribute>();
        methods = new ArrayList<Method>();                
    }
    
    public boolean isbAstract() {
        return bAstract;
    }

    public void setAbstract(boolean bAstract) {
        this.bAstract = bAstract;
    }    
    
    public void addAttribute(Attribute a)
    {
        this.attributes.add(a);
    }
    
    public void addMethod(Method m)
    {
        this.methods.add(m);
    }
    
    public ArrayList<Attribute> getAttributes()
    {
        return this.attributes;
    }
    
    public ArrayList<Method> getMethods(){
        return this.methods;
    }
    
    //determinar el ancho del rectangulo
    private int getMaxWidth()
    {
        int ret=0, attMax=0, aux=0, metMax=0;
        for(Attribute at: attributes)
        {
            aux = at.getName().length() + at.getType().length();
            if(aux > attMax)
                attMax = aux;
        }        
        aux=0;
        for(Method m : methods)
        {
            aux = m.toString().length();
            if(aux > metMax)
                metMax = aux;
        }
        if (getName().length() > attMax)
            if(getName().length() > metMax)
                ret = getName().length();
            else
                ret = metMax;
        else
            if(attMax > metMax)
                ret = attMax;
            else
                ret = metMax;
        return ret;
    }

    //TODO: cambiar nombres para confundirse
    private int getMinWidth(){
        return (this.getMaxWidth()+3)*LETTER;
    }
    
    //altura maxima del rectangulo que encierra la clase
    private int getMaxHeigth() {
        int aux = 0;
        //nombre de la clase
        aux += LHEIGHT;
        //espacio  de los atributos
        aux+= attributes.size() * LHEIGHT +LHEIGHT;
        //espacio ocupado por los metodos
        aux+=methods.size() * LHEIGHT + LHEIGHT;
        return aux;
    }
    
    @Override
    public void paint(Graphics g2) {
        Graphics2D g = (Graphics2D) g2;        
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);        
        
        String buffer = "";
        Font mfont;        
        int cont=1,x,y, attrH;       
        x = this.getPosX();
        y = this.getPosY();
        
        //se calcula el ancho de la clase                
        if(Needrefresh())
        {
            //la cantidad maxima de caracteres que se deben pintar el rectangulo
            this.width = this.getMaxWidth() + 3;            
            //set ancho del rect
            //si el ancho que tiene el rectangulo, es mayor que el que se calcula (por el resize)        
            if(getWidth() < (this.width*LETTER) )
                setWidth(this.width*LETTER);
            this.height = this.getMaxHeigth();
            //altura
            if(getHeight() < this.height)
                setHeight(this.height);
            setRefresh(false);
        }
        height = 0;
        //nombre de la clase
        g.setColor(getLineColor());
        g.drawRect(x, y, getWidth(), LHEIGHT);
        g.setColor(getFillColor());
        g.fillRect(x+1, y+1, getWidth()-1, LHEIGHT-1);        
        
        this.height += LHEIGHT;
        if(this.bAstract)
            g.setFont(Constants.italicBoldFont);
        else
            g.setFont(Constants.boldFont);
        
        //centrar el nombre de la clase
        g.setColor(Constants.DEFAULTCOLOR);
        PaintUtils.drawCenterString(g, getName(), x, y+15, getWidth());
        
        //atributos de la clase
        g.setColor(getLineColor());
        g.setFont(Constants.defaultFont);
        g.drawRect(x, y+LHEIGHT, getWidth() , attributes.size() * LHEIGHT +LHEIGHT);
        g.setColor(getFillColor());
        g.fillRect(x+1, y+LHEIGHT+1, getWidth()-1 , (attributes.size() * LHEIGHT +LHEIGHT)-1);
        g.setColor(getLineColor());
        //
        this.height+= attributes.size() * LHEIGHT +LHEIGHT;
        //
        g.setColor(Color.BLACK);
        attrH = attributes.size() * LHEIGHT +LHEIGHT;
        for(Attribute att:attributes)
        { 
            mfont = Constants.defaultFont;
            if(att.isStatic())
                mfont = Constants.underlineFont;            
            buffer = ""+ getVisibilitySymbol(att.getVisibility())+" "+att.getName()+" : "+att.getType();
            g.setFont(mfont);
            g.drawString(buffer, x+5, y+LHEIGHT+ (cont*LHEIGHT));
            buffer="";
            cont++;
        }        
        //metodos
        buffer="";
        cont=1;
        g.setColor(getLineColor());
        //resizar el tamanio de los metodos para concordar con el resize
        int newidth,tamattrib=attrH+LHEIGHT;
        if(getHeight()> tamattrib)
            newidth = getHeight() - tamattrib;
        else
            newidth = methods.size() * LHEIGHT + LHEIGHT;
        
        g.drawRect(x, y+attrH+LHEIGHT, getWidth(), newidth);
        g.setColor(getFillColor());
        g.fillRect(x+1, y+attrH+LHEIGHT+1, getWidth()-1, newidth-1);
        g.setColor(getLineColor());
        //alto del rectangulo
        this.height+=methods.size() * LHEIGHT + LHEIGHT;
        //evitar que el se vuelva a calcular despues de un resize
        if(getHeight() < height)
            setHeight(height);
        //        
        g.setColor(Color.BLACK);
        for(Method mtd: methods)
        {
            mfont = Constants.defaultFont;
            //cuando el metodo es statico o abstract
            if(mtd.isStatic())
                mfont = Constants.underlineFont;
            if(mtd.isAbstrac()){
                mfont = Constants.italicFont;
                if(mtd.isStatic())
                    mfont = Constants.italicUnderlineFont;
            }                        
            g.setFont(mfont);
            buffer = ""+ getVisibilitySymbol(mtd.getVisibility())+" "+ mtd.getName() + "(";
            numparams = mtd.getParams().size();
            int contparams = 1;
            for(Param param : mtd.getParams())
            {
                buffer+=param.getType();
                if(contparams<numparams)
                    buffer+=",";
                contparams++;
            }
            buffer+= ") : "+mtd.getType();
            g.drawString(buffer, x+5, y+ attrH + cont*LHEIGHT +LHEIGHT);
            cont++;
            buffer="";
        }
        
        //seleccionada
        if(isSelected())
            paintSelected(g);
    }        
    
    //prueba
    public Object clone(){
        Object clon = null;        
        clon = super.clone();
        ((Clase)clon).setMethods((ArrayList<Method>)methods.clone());
        ((Clase)clon).setAttributes((ArrayList<Attribute>)attributes.clone());
        return clon;
    }
    //metodos para permitir la clonacion
    protected void setMethods(ArrayList<Method> m){
        this.methods = m;
    }
    
    protected void setAttributes(ArrayList<Attribute> a){
        this.attributes = a;
    }
    
    @Override
    public void resize(Rectangle r){
        setPosX(r.x);
        setPosY(r.y);
        if(r.width > this.getMinWidth())            
            setWidth(r.width);
        if(r.height > this.getMaxHeigth())
            setHeight(r.height);
    }
    
    private String getVisibilitySymbol(VisibilityKind vis){
        String ret = "+";
        
        if(vis == VisibilityKind.V_PRIVATE) ret = "-";            
        else if(vis == VisibilityKind.V_PROTECTED) ret = "#";
        else if(vis == VisibilityKind.V_PACKAGE) ret = "~";
        
        return ret;
    }
    
    
    public Element xmiSerialize(){
        Namespace xmins = Constants.XMINAMESPACE;
        Element clase = new Element("packagedElement");
        clase.setAttribute("type","uml:Class",xmins);
        clase.setAttribute("id",getId(),xmins);
        clase.setAttribute("name",getName());
        clase.setAttribute("visibility","public");
        if(isbAstract())
          clase.setAttribute("isAbstract","true");  
        
        //export attributes
        for(Attribute a : attributes){
            a.xmiSerialize(clase);
        }
        //export methods
        for(Method m : methods){
            m.xmiSerialize(clase);
        }
        
        //parent.addContent(clase);
        return clase;
    }
    
}
